﻿using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Windows.Controls;
using System.Windows.Media;
using LukasKopenec.LocalizationSupport;
using Notoric.Commanding.CommandBase;
using Notoric.Model.Elements;
using Notoric.Model.Packaging;
using Notoric.ViewModel.Internal;

namespace Notoric.ViewModel.Elements
{
    /// <summary>
    /// Adapts the <see cref="ScorePart"/>.
    /// </summary>
    public class Score : ViewModelElement, IProjectExplorerItem
    {
        #region Constructors

        /// <summary>
        /// Creates a new <see cref="Score"/> instance.
        /// </summary>
        /// <param name="adaptedScore">
        /// <para>
        /// Type: <see cref="ScorePart"/>
        /// </para>
        /// <para>
        /// The score package part adapted by this element.
        /// </para>
        /// </param>
        /// <param name="name">
        /// <para>
        /// Type: <see cref="System.String"/>
        /// </para>
        /// <para>
        /// The name of the score.
        /// </para>
        /// </param>
        /// <param name="owner">
        /// <para>
        /// Type: <see cref="Project"/>
        /// </para>
        /// <para>
        /// The project that directly owns this score.
        /// </para>
        /// </param>
        /// <param name="undoStack">
        /// <para>
        /// Type: <see cref="CommandStack"/>
        /// </para>
        /// <para>
        /// The undo command stack for this score.
        /// </para>
        /// </param>
        /// <param name="redoStack">
        /// <para>
        /// Type: <see cref="CommandStack"/>
        /// </para>
        /// <para>
        /// The redo command stack for this score.
        /// </para>
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="adaptedScore"/>, <paramref name="undoStack"/> or <paramref name="redoStack"/> is <see langword="null"/>.
        /// </exception>
        public Score(ScorePart adaptedScore, string name, Project owner, CommandStack undoStack, CommandStack redoStack)
            : base(adaptedScore, owner, undoStack, redoStack)
        {
            if (owner == null)
                throw new ArgumentNullException("owner");

            this.name = name;
            var pathElement = adaptedScore.Package.ProjectPart.Project.Scores.First(pe => pe.URI == adaptedScore.Uri);
            pathElement.PropertyChanged += new PropertyChangedEventHandler(pathElement_PropertyChanged);
            pages = new ObservableCollection<Page>();
            adaptedScore.Score.Pages.CollectionChanged += new EventHandler<Model.Collections.NotifyCollectionChangedEventArgs>(Pages_CollectionChanged);

            projectExplorerHelper = new ProjectExplorerItemHelper(owner, this);
            projectExplorerHelper.SetIcon(EmbeddedBitmapLoader.Load("Pictures.ProjectExplorer.Score.png"));
            projectExplorerHelper.PropertyChanged += new PropertyChangedEventHandler(projectExplorerHelper_PropertyChanged);
            projectExplorerHelper.CreateHierarchy(((ScoreElement)AdaptedElement).Pages, CreatePageLeafElement, null);

            CreateContextMenu();
        }

        #endregion

        #region ViewModelElement Members

        /// <inheritdoc/>
        public override string ElementNameKey
        {
            get { return elementNameKey; }
        }

        #endregion

        #region IProjectExplorerItem Members

        /// <inheritdoc/>
        public ContextMenu ContextMenu
        {
            get { return projectExplorerHelper.ContextMenu; }
        }

        /// <inheritdoc/>
        public string DisplayName
        {
            get { return Name; }
        }

        /// <inheritdoc/>
        public ImageSource Icon
        {
            get { return projectExplorerHelper.Icon; }
        }

        /// <inheritdoc/>
        public bool IsExpanded
        {
            get
            {
                return projectExplorerHelper.IsExpanded;
            }
            set
            {
                projectExplorerHelper.IsExpanded = value;
            }
        }

        /// <inheritdoc/>
        public bool IsInEditMode
        {
            get { return projectExplorerHelper.IsInEditMode; }
            set { projectExplorerHelper.IsInEditMode = value; }
        }

        /// <inheritdoc/>
        public bool IsSelected
        {
            get
            {
                return projectExplorerHelper.IsSelected;
            }
            set
            {
                projectExplorerHelper.IsSelected = value;
            }
        }

        /// <inheritdoc/>
        public string Name
        {
            get { return name; }
            set
            {
                var cmd = new RenamePartCommand(UndoStack, RedoStack)
                {
                    PathCollection = AdaptedPart.Package.ProjectPart.Project.Scores,
                    RenamedPart = this,
                    VirtualPath = ((Directory)Parent).VirtualPath + "/" + value
                };
                cmd.Execute(null);

                RaisePropertyChanged("Name");
                RaisePropertyChanged("DisplayName");
            }
        }

        /// <inheritdoc/>
        public IProjectExplorerItem Parent
        {
            get { return projectExplorerHelper.Parent; }
            set { projectExplorerHelper.Parent = value; }
        }

        /// <inheritdoc/>
        public ObservableCollection<IProjectExplorerItem> Children
        {
            get { return projectExplorerHelper.Children; }
        }

        #endregion

        #region Event Handlers

        /// <summary>
        /// Occurs when the collection of pages of the adapted element has changed.
        /// </summary>
        /// <param name="sender">Object that has sent the event</param>
        /// <param name="e">Event data</param>
        private void Pages_CollectionChanged(object sender, Model.Collections.NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case Model.Collections.NotifyCollectionChangedAction.Add:
                {
                    int index = e.NewStartingIndex;
                    foreach (Model.Collections.PathElement pathElement in e.NewItems)
                    {
                        var pagePart = (Model.Packaging.PagePart)AdaptedPart.Package.GetPart(pathElement.URI);
                        var pageName = pathElement.VirtualPath.Substring(pathElement.VirtualPath.LastIndexOf('/') + 1);
                        var page = new Page(pagePart, this, pageName);

                        pages.Insert(index, page);
                        projectExplorerHelper.Children.Insert(index++, page);
                    }
                    break;
                }
                case Model.Collections.NotifyCollectionChangedAction.Remove:
                    foreach (Model.Collections.PathElement pathElement in e.OldItems)
                    {
                        var pageName = pathElement.VirtualPath.Substring(pathElement.VirtualPath.LastIndexOf('/') + 1);
                        var pageItem = pages.FirstOrDefault(page => page.Name == pageName);
                        if (pageItem != null)
                        {
                            pages.Remove(pageItem);
                            projectExplorerHelper.Children.Remove(pageItem);
                        }
                    }
                    break;
            }
        }

        /// <summary>
        /// Occurs when the virtual path to this score has changed.
        /// </summary>
        /// <param name="sender">Object that has sent the event</param>
        /// <param name="e">Event data</param>
        private void pathElement_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "VirtualPath")
            {
                var path = ((Model.Collections.PathElement)sender).VirtualPath;
                name = path.Substring(path.LastIndexOf('/') + 1);

                RaisePropertyChanged("Name");
                RaisePropertyChanged("DisplayName");
            }
        }

        /// <summary>
        /// Occurs when the value of some property of the <see cref="projectExplorerHelper"/> has changed.
        /// </summary>
        /// <remarks>
        /// <b>Operation:</b><br/>
        /// The handler just resends the event via its own <see cref="ViewModelElement.PropertyChanged"/>.
        /// </remarks>
        /// <param name="sender">The object that has sent the event</param>
        /// <param name="e">Event data</param>
        private void projectExplorerHelper_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            RaisePropertyChanged(e);
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Creates the context menu for this score.
        /// </summary>
        private void CreateContextMenu()
        {
            var menu = new ContextMenu();
            var mitem = new MenuItem();
            mitem.Icon = EmbeddedBitmapLoader.LoadImage("Pictures.ProjectExplorer.NewDocument.png", 16, 16);
            mitem.Header = (string)LocalizationManager.GetValue("ProjectExplorer.CommandTitles.NewPage");
            mitem.Command = ProjectExplorerCommands.NewPageCommand;
            mitem.CommandParameter = this;
            menu.Items.Add(mitem);

            mitem = new MenuItem();
            mitem.Icon = EmbeddedBitmapLoader.LoadImage("Pictures.ProjectExplorer.EditMetadata.png", 16, 16);
            mitem.Header = (string)LocalizationManager.GetValue("ProjectExplorer.CommandTitles.EditMetadata");
            mitem.Command = ProjectExplorerCommands.EditMetadataCommand;
            mitem.CommandParameter = this;
            menu.Items.Add(mitem);

            mitem = new MenuItem();
            mitem.Header = (string)LocalizationManager.GetValue("ProjectExplorer.CommandTitles.Rename");
            mitem.Command = ProjectExplorerCommands.RenameCommand;
            mitem.CommandParameter = this;
            menu.Items.Add(mitem);

            menu.Items.Add(new Separator());

            mitem = new MenuItem();
            mitem.Icon = EmbeddedBitmapLoader.LoadImage("Pictures.ProjectExplorer.Delete.png", 16, 16);
            mitem.Header = (string)LocalizationManager.GetValue("ProjectExplorer.CommandTitles.Delete");
            mitem.Command = ProjectExplorerCommands.DeleteCommand;
            mitem.CommandParameter = this;
            menu.Items.Add(mitem);

            projectExplorerHelper.SetContextMenu(menu);
        }

        /// <summary>
        /// Creates a <see cref="Page"/> that will adapt a given <see cref="PagePart"/> at a given URI under a given name.
        /// </summary>
        /// <param name="pageName">The name of the page</param>
        /// <param name="pagePartUri">URI of the adapted page part</param>
        /// <returns>
        /// <para>
        /// Type: <see cref="IProjectExplorerItem"/>
        /// </para>
        /// <para>
        /// The created <see cref="Page"/>.
        /// </para>
        /// </returns>
        private IProjectExplorerItem CreatePageLeafElement(string pageName, Uri pagePartUri)
        {
            var part = (PagePart)AdaptedPart.Package.GetPart(pagePartUri);

            var page = new Page(part, this, pageName);
            pages.Add(page);

            return page;
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Creates an undoable command for setting the date associated with the score.
        /// </summary>
        /// <param name="date">
        /// <para>
        /// Type: <see cref="String"/>
        /// </para>
        /// <para>
        /// The date.
        /// </para>
        /// </param>
        /// <returns>
        /// <para>
        /// Type: <see cref="UndoableCommandBase"/>
        /// </para>
        /// <para>
        /// The created command ready for use.
        /// </para>
        /// </returns>
        public UndoableCommandBase CreateSetDateCommand(string date)
        {
            return new SetterCommand<string>(this, "Date", date, UndoStack, RedoStack)
            {
                Name = "Change Score Date"
            };
        }

        /// <summary>
        /// Creates an undoable command for setting the default page height.
        /// </summary>
        /// <param name="height">
        /// <para>
        /// Type: <see cref="Double"/>
        /// </para>
        /// <para>
        /// The heigth.
        /// </para>
        /// </param>
        /// <returns>
        /// <para>
        /// Type: <see cref="UndoableCommandBase"/>
        /// </para>
        /// <para>
        /// The created command ready for use.
        /// </para>
        /// </returns>
        public UndoableCommandBase CreateSetDefaultPageHeightCommand(double height)
        {
            var modelHeight = (double)LengthConverter.ConvertBack(height, typeof(double), null, CultureInfo.CurrentCulture);

            return new SetterCommand<double>(this, "DefaultPageHeight", modelHeight, UndoStack, RedoStack)
            {
                Name = "Change Default Page Height"
            };
        }

        /// <summary>
        /// Creates an undoable command for setting the default page width.
        /// </summary>
        /// <param name="width">
        /// <para>
        /// Type: <see cref="Double"/>
        /// </para>
        /// <para>
        /// The width.
        /// </para>
        /// </param>
        /// <returns>
        /// <para>
        /// Type: <see cref="UndoableCommandBase"/>
        /// </para>
        /// <para>
        /// The created command ready for use.
        /// </para>
        /// </returns>
        public UndoableCommandBase CreateSetDefaultPageWidthCommand(double width)
        {
            var modelWidth = (double)LengthConverter.ConvertBack(width, typeof(double), null, CultureInfo.CurrentCulture);

            return new SetterCommand<double>(this, "DefaultPageWidth", modelWidth, UndoStack, RedoStack)
            {
                Name = "Change Default Page Width"
            };
        }

        /// <summary>
        /// Creates an undoable command for setting the description of the score.
        /// </summary>
        /// <param name="description">
        /// <para>
        /// Type: <see cref="String"/>
        /// </para>
        /// <para>
        /// The description.
        /// </para>
        /// </param>
        /// <returns>
        /// <para>
        /// Type: <see cref="UndoableCommandBase"/>
        /// </para>
        /// <para>
        /// The created command ready for use.
        /// </para>
        /// </returns>
        public UndoableCommandBase CreateSetDescriptionCommand(string description)
        {
            return new SetterCommand<string>(this, "Description", description, UndoStack, RedoStack)
            {
                Name = "Change Score Description"
            };
        }

        /// <summary>
        /// Creates an undoable command for setting the identifier of the score.
        /// </summary>
        /// <param name="identifier">
        /// <para>
        /// Type: <see cref="String"/>
        /// </para>
        /// <para>
        /// The identifier.
        /// </para>
        /// </param>
        /// <returns>
        /// <para>
        /// Type: <see cref="UndoableCommandBase"/>
        /// </para>
        /// <para>
        /// The created command ready for use.
        /// </para>
        /// </returns>
        public UndoableCommandBase CreateSetIdentifierCommand(string identifier)
        {
            return new SetterCommand<string>(this, "Identifier", identifier, UndoStack, RedoStack)
            {
                Name = "Change Score Identifier"
            };
        }

        /// <summary>
        /// Creates an undoable command for setting the language of the score.
        /// </summary>
        /// <param name="language">
        /// <para>
        /// Type: <see cref="String"/>
        /// </para>
        /// <para>
        /// The language.
        /// </para>
        /// </param>
        /// <returns>
        /// <para>
        /// Type: <see cref="UndoableCommandBase"/>
        /// </para>
        /// <para>
        /// The created command ready for use.
        /// </para>
        /// </returns>
        public UndoableCommandBase CreateSetLanguageCommand(string language)
        {
            return new SetterCommand<string>(this, "Language", language, UndoStack, RedoStack)
            {
                Name = "Change Score Language"
            };
        }

        /// <summary>
        /// Creates an undoable command for setting the publisher of the score.
        /// </summary>
        /// <param name="publisher">
        /// <para>
        /// Type: <see cref="String"/>
        /// </para>
        /// <para>
        /// The publisher.
        /// </para>
        /// </param>
        /// <returns>
        /// <para>
        /// Type: <see cref="UndoableCommandBase"/>
        /// </para>
        /// <para>
        /// The created command ready for use.
        /// </para>
        /// </returns>
        public UndoableCommandBase CreateSetPublisherCommand(string publisher)
        {
            return new SetterCommand<string>(this, "Publisher", publisher, UndoStack, RedoStack)
            {
                Name = "Change Score Publisher"
            };
        }

        /// <summary>
        /// Creates an undoable command for setting the rights of the score.
        /// </summary>
        /// <param name="rights">
        /// <para>
        /// Type: <see cref="String"/>
        /// </para>
        /// <para>
        /// The rights.
        /// </para>
        /// </param>
        /// <returns>
        /// <para>
        /// Type: <see cref="UndoableCommandBase"/>
        /// </para>
        /// <para>
        /// The created command ready for use.
        /// </para>
        /// </returns>
        public UndoableCommandBase CreateSetRightsCommand(string rights)
        {
            return new SetterCommand<string>(this, "Rights", rights, UndoStack, RedoStack)
            {
                Name = "Change Score Rights"
            };
        }

        /// <summary>
        /// Creates an undoable command for setting the subject of the score.
        /// </summary>
        /// <param name="subject">
        /// <para>
        /// Type: <see cref="String"/>
        /// </para>
        /// <para>
        /// The subject.
        /// </para>
        /// </param>
        /// <returns>
        /// <para>
        /// Type: <see cref="UndoableCommandBase"/>
        /// </para>
        /// <para>
        /// The created command ready for use.
        /// </para>
        /// </returns>
        public UndoableCommandBase CreateSetSubjectCommand(string subject)
        {
            return new SetterCommand<string>(this, "Subject", subject, UndoStack, RedoStack)
            {
                Name = "Change Score Subject"
            };
        }

        /// <summary>
        /// Creates an undoable command for setting the subtitle of the score.
        /// </summary>
        /// <param name="subtitle">
        /// <para>
        /// Type: <see cref="String"/>
        /// </para>
        /// <para>
        /// The subtitle.
        /// </para>
        /// </param>
        /// <returns>
        /// <para>
        /// Type: <see cref="UndoableCommandBase"/>
        /// </para>
        /// <para>
        /// The created command ready for use.
        /// </para>
        /// </returns>
        public UndoableCommandBase CreateSetSubTitleCommand(string subtitle)
        {
            return new SetterCommand<string>(this, "SubTitle", subtitle, UndoStack, RedoStack)
            {
                Name = "Change Score SubTitle"
            };
        }

        /// <summary>
        /// Creates an undoable command for setting the title of the score.
        /// </summary>
        /// <param name="title">
        /// <para>
        /// Type: <see cref="String"/>
        /// </para>
        /// <para>
        /// The title.
        /// </para>
        /// </param>
        /// <returns>
        /// <para>
        /// Type: <see cref="UndoableCommandBase"/>
        /// </para>
        /// <para>
        /// The created command ready for use.
        /// </para>
        /// </returns>
        public UndoableCommandBase CreateSetTitleCommand(string title)
        {
            return new SetterCommand<string>(this, "Title", title, UndoStack, RedoStack)
            {
                Name = "Change Score Title"
            };
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or sets the date associated with the score.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="string"/>
        /// </para>
        /// <para>
        /// A date associated with an event in the life cycle of the resource.
        /// Typically, Date will be associated with the creation or availability 
        /// of the resource. Recommended best practice for encoding the date 
        /// value is defined in a profile of ISO 8601 [W3CDTF] and follows the YYYY-MM-DD format.
        /// </para>
        /// </value>
        public string Date
        {
            get { return GetPropertyValue<string>("Date"); }
            set
            {
                if (UnsavedChanges.ContainsKey("Date"))
                {
                    var cmd = (SetterCommand<string>)UnsavedChanges["Date"];
                    cmd.Value = value;
                }
                else
                    UnsavedChanges["Date"] = CreateSetDateCommand(value);

                RaisePropertyChanged("Date");
            }
        }

        /// <summary>
        /// Gets or sets the default height of a score page.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="double"/>
        /// </para>
        /// <para>
        /// The default height (in device independent pixels) of a score page.
        /// </para>
        /// </value>
        public double DefaultPageHeight
        {
            get { return (double)LengthConverter.Convert(GetPropertyValue<double>("DefaultPageHeight"), typeof(double), null, CultureInfo.CurrentCulture); }
            set
            {
                if (UnsavedChanges.ContainsKey("DefaultPageHeight"))
                {
                    var cmd = (SetterCommand<double>)UnsavedChanges["DefaultPageHeight"];
                    cmd.Value = (double)LengthConverter.ConvertBack(value, typeof(double), null, CultureInfo.CurrentCulture);
                }
                else
                    UnsavedChanges["DefaultPageHeight"] = CreateSetDefaultPageHeightCommand(value);

                RaisePropertyChanged("DefaultPageHeight");
            }
        }

        /// <summary>
        /// Gets or sets the default width of a score page.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="double"/>
        /// </para>
        /// <para>
        /// The default width (in device independent pixels) of a score page.
        /// </para>
        /// </value>
        public double DefaultPageWidth
        {
            get { return (double)LengthConverter.Convert(GetPropertyValue<double>("DefaultPageWidth"), typeof(double), null, CultureInfo.CurrentCulture); }
            set
            {
                if (UnsavedChanges.ContainsKey("DefaultPageWidth"))
                {
                    var cmd = (SetterCommand<double>)UnsavedChanges["DefaultPageWidth"];
                    cmd.Value = (double)LengthConverter.ConvertBack(value, typeof(double), null, CultureInfo.CurrentCulture);
                }
                else
                    UnsavedChanges["DefaultPageWidth"] = CreateSetDefaultPageWidthCommand(value);

                RaisePropertyChanged("DefaultPageWidth");
            }
        }

        /// <summary>
        /// Gets or sets the description of the score.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="string"/>
        /// </para>
        /// <para>
        /// An account of the content of the resource.
        /// Description may include but is not limited to: an abstract, table of contents, 
        /// reference to a graphical representation of content or a free-text account of the content.
        /// </para>
        /// </value>
        public string Description
        {
            get { return GetPropertyValue<string>("Description"); }
            set
            {
                if (UnsavedChanges.ContainsKey("Description"))
                {
                    var cmd = (SetterCommand<string>)UnsavedChanges["Description"];
                    cmd.Value = value;
                }
                else
                    UnsavedChanges["Description"] = CreateSetDescriptionCommand(value);

                RaisePropertyChanged("Description");
            }
        }

        /// <summary>
        /// Gets or sets the identifier f the score.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="string"/>
        /// </para>
        /// <para>
        /// An unambiguous reference to the resource within a given context.
        /// Recommended best practice is to identify the resource by means 
        /// of a string or number conforming to a formal identification system. 
        /// Example formal identification systems include the Uniform Resource Identifier (URI) 
        /// (including the Uniform Resource Locator (URL)), the Digital Object Identifier (DOI) 
        /// and the International Standard Book Number (ISBN).
        /// </para>
        /// </value>
        public string Identifier
        {
            get { return GetPropertyValue<string>("Identifier"); }
            set
            {
                if (UnsavedChanges.ContainsKey("Identifier"))
                {
                    var cmd = (SetterCommand<string>)UnsavedChanges["Identifier"];
                    cmd.Value = value;
                }
                else
                    UnsavedChanges["Identifier"] = CreateSetIdentifierCommand(value);

                RaisePropertyChanged("Identifier");
            }
        }

        /// <summary>
        /// Gets or sets the language of the score.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="string"/>
        /// </para>
        /// <para>
        /// A language of the intellectual content of the resource.
        /// Recommended best practice for the values of the Language 
        /// element is defined by RFC 1766 [RFC1766] which includes 
        /// a two-letter Language Code (taken from the ISO 639 standard [ISO639]), 
        /// followed, optionally, by a two-letter Country Code 
        /// (taken from the ISO 3166 standard [ISO3166]).
        /// For example, 'en' for English, 'fr' for French, or 'en-uk' 
        /// for English used in the United Kingdom.
        /// </para>
        /// </value>
        public string Language
        {
            get { return GetPropertyValue<string>("Language"); }
            set
            {
                if (UnsavedChanges.ContainsKey("Language"))
                {
                    var cmd = (SetterCommand<string>)UnsavedChanges["Language"];
                    cmd.Value = value;
                }
                else
                    UnsavedChanges["Language"] = CreateSetLanguageCommand(value);

                RaisePropertyChanged("Language");
            }
        }

        /// <summary>
        /// Gets the collection of pages in this score.
        /// </summary>
        public ObservableCollection<Page> Pages
        {
            get { return pages; }
        }

        /// <summary>
        /// Gets or sets the publisher of the score.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="string"/>
        /// </para>
        /// <para>
        /// An entity responsible for making the resource available.
        /// Examples of a Publisher include a person, an organization, 
        /// or a service. Typically, the name of a Publisher should be 
        /// used to indicate the entity.
        /// </para>
        /// </value>
        public string Publisher
        {
            get { return GetPropertyValue<string>("Publisher"); }
            set
            {
                if (UnsavedChanges.ContainsKey("Publisher"))
                {
                    var cmd = (SetterCommand<string>)UnsavedChanges["Publisher"];
                    cmd.Value = value;
                }
                else
                    UnsavedChanges["Publisher"] = CreateSetPublisherCommand(value);

                RaisePropertyChanged("Publisher");
            }
        }

        /// <summary>
        /// Gets or sets the rights over the score.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="string"/>
        /// </para>
        /// <para>
        /// Information about rights held in and over the resource.
        /// Typically, a Rights element will contain a rights management 
        /// statement for the resource, or reference a service providing 
        /// such information. Rights information often encompasses 
        /// Intellectual Property Rights (IPR), Copyright, and various Property Rights. 
        /// If the Rights element is absent, no assumptions can be made about the status 
        /// of these and other rights with respect to the resource.
        /// </para>
        /// </value>
        public string Rights
        {
            get { return GetPropertyValue<string>("Rights"); }
            set
            {
                if (UnsavedChanges.ContainsKey("Rights"))
                {
                    var cmd = (SetterCommand<string>)UnsavedChanges["Rights"];
                    cmd.Value = value;
                }
                else
                    UnsavedChanges["Rights"] = CreateSetRightsCommand(value);

                RaisePropertyChanged("Rights");
            }
        }

        /// <summary>
        /// Gets or sets the subject of the score.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="string"/>
        /// </para>
        /// <para>
        /// The topic of the content of the resource.
        /// Typically, a Subject will be expressed as keywords, key phrases or classification codes 
        /// that describe a topic of the resource
        /// </para>
        /// <para>
        /// Recommended best practice is to select a value from a controlled vocabulary 
        /// or formal classification scheme. Subject headings, usually from controlled 
        /// vocabularies such as LCSH, LCTGM, AAT, etc. Also local vocabularies.
        /// </para>
        /// </value>
        public string Subject
        {
            get { return GetPropertyValue<string>("Subject"); }
            set
            {
                if (UnsavedChanges.ContainsKey("Subject"))
                {
                    var cmd = (SetterCommand<string>)UnsavedChanges["Subject"];
                    cmd.Value = value;
                }
                else
                    UnsavedChanges["Subject"] = CreateSetSubjectCommand(value);

                RaisePropertyChanged("Subject");
            }
        }

        /// <summary>
        /// Gets or sets the sub title of the score.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="string"/>
        /// </para>
        /// <para>
        /// The sub title may optionally extend the <see cref="Title"/>.
        /// </para>
        /// </value>
        public string SubTitle
        {
            get { return GetPropertyValue<string>("SubTitle"); }
            set
            {
                if (UnsavedChanges.ContainsKey("SubTitle"))
                {
                    var cmd = (SetterCommand<string>)UnsavedChanges["SubTitle"];
                    cmd.Value = value;
                }
                else
                    UnsavedChanges["SubTitle"] = CreateSetSubTitleCommand(value);

                RaisePropertyChanged("SubTitle");
            }
        }

        /// <summary>
        /// Gets or sets the title of the score.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="string"/>
        /// </para>
        /// <para>
        /// A name given to the resource. Typically, a Title will be a name by which the resource is formally known.
        /// </para>
        /// </value>
        public string Title
        {
            get { return GetPropertyValue<string>("Title"); }
            set
            {
                if (UnsavedChanges.ContainsKey("Title"))
                {
                    var cmd = (SetterCommand<string>)UnsavedChanges["Title"];
                    cmd.Value = value;
                }
                else
                    UnsavedChanges["Title"] = CreateSetTitleCommand(value);

                RaisePropertyChanged("Title");
            }
        }

        #endregion

        #region Private Fields

        /// <summary>
        /// Holds a user friendly name of this element.
        /// </summary>
        private const string elementNameKey = "ViewModel.ElementNames.Score";
        /// <summary>
        /// Holds the name of this score.
        /// </summary>
        private string name;
        /// <summary>
        /// Holds the collection of pages in this score.
        /// </summary>
        private ObservableCollection<Page> pages;
        /// <summary>
        /// Holds the project explorer helper implementation.
        /// </summary>
        private ProjectExplorerItemHelper projectExplorerHelper;

        #endregion
    }
}
